package modl

import (
	"bufio"
	"bytes"
	"fmt"
	"time"
)

type MDODiff struct {
	persistable          bool
	AttributeDifferences []AttributeDiff
}

func (d *MDODiff) Add(attributeDiff AttributeDiff) {
	d.AttributeDifferences = append(d.AttributeDifferences, attributeDiff)
	if attributeDiff.isPersistable() {
		d.persistable = true
	}
}

func (d *MDODiff) Serialize(w *bufio.Writer, isPersistentWrite bool) {
	if isPersistentWrite && !d.persistable {
		return
	}
	WriteBool(d.persistable, w, "persistable")
	WriteUint32(uint32(len(d.AttributeDifferences)), w, "attribute diff count")
	for _, diff := range d.AttributeDifferences {
		diff.serialize(w, isPersistentWrite)
	}
}

func (d *MDODiff) Empty() bool {
	return len(d.AttributeDifferences) == 0
}

func (d *MDODiff) Len() int {
	return len(d.AttributeDifferences)
}

func DeserializeMDODiff(r *bufio.Reader, s *Schema) *MDODiff {
	d := new(MDODiff)
	d.persistable = ReadBool(r, "persistable")
	attributeDiffCount := ReadUint32(r, "attribute diff count")
	var i uint32
	for i = 0; i < attributeDiffCount; i++ {
		d.Add(deserializeAttributeDiff(r, s))
	}
	return d
}

type AttributeDiff interface {
	GetAttributeId() (uint32, uint32)
	isPersistable() bool
	serialize(*bufio.Writer, bool)
	deserialize(r *bufio.Reader, s *Schema)
}

type AttributeDiffBase struct {
	typeId      uint32
	attributeId uint32
	persistable bool
}

func (d *AttributeDiffBase) init(typeId, attributeId uint32, persistable bool) {
	d.typeId = typeId
	d.attributeId = attributeId
	d.persistable = persistable
}

func (d *AttributeDiffBase) GetAttributeId() (typeId, attributeId uint32) {
	return d.typeId, d.attributeId
}

func (d *AttributeDiffBase) isPersistable() bool {
	return d.persistable
}

func (d *AttributeDiffBase) serialize(w *bufio.Writer, isPersistentWrite bool) {
	if isPersistentWrite && !d.persistable {
		return
	}
	defer func() {
		if r := recover(); r != nil {
			panic(fmt.Sprintf(""))
		}
	}()
	WriteUint32(d.typeId, w, "typeId")
	WriteUint32(d.attributeId, w, "attributeId")
	WriteBool(d.persistable, w, "persistable")
}

func deserializeAttributeDiff(r *bufio.Reader, s *Schema) AttributeDiff {
	typeId := ReadUint32(r, "typeId")
	typeDef, ok := s.Types[typeId]
	if !ok {
		panic(fmt.Sprintf("Unknown type id %d", typeId))
	}
	attributeId := ReadUint32(r, "attributeId")
	attributeDef, ok := typeDef.attributeDefs[attributeId]
	if !ok {
		panic(fmt.Sprintf("Unknown attribute id %d in type %s[%d]", attributeId, typeDef.Name, typeId))
	}
	persistable := ReadBool(r, "persistable")
	diff := attributeDef.NewAttributeDiff(typeId, attributeId, persistable)
	diff.deserialize(r, s)
	return diff
}

type ByteDiff struct {
	AttributeDiffBase
	OldValue byte
	NewValue byte
}

func NewBaseByteDiff(typeId, attributeId uint32, persistable bool) AttributeDiff {
	d := new(ByteDiff)
	d.AttributeDiffBase.init(typeId, attributeId, persistable)
	return d
}

func NewByteDiff(typeId, attributeId uint32, persistable bool, oldValue, newValue byte) AttributeDiff {
	d := new(ByteDiff)
	d.AttributeDiffBase.init(typeId, attributeId, persistable)
	d.OldValue = oldValue
	d.NewValue = newValue
	return d
}

func NewByteSliceAddition(newValue byte) AttributeDiff {
	d := new(ByteDiff)
	d.NewValue = newValue
	return d
}

func NewByteSliceModification(oldValue, newValue byte) AttributeDiff {
	d := new(ByteDiff)
	d.OldValue = oldValue
	d.NewValue = newValue
	return d
}

func (d *ByteDiff) Serialize(w *bufio.Writer, isPersistentWrite bool) {
	d.AttributeDiffBase.serialize(w, isPersistentWrite)
	WriteByte(1+1, w, "diff length")
	WriteByte(d.OldValue, w, "old value")
	WriteByte(d.NewValue, w, "new value")
}

func (d *ByteDiff) deserialize(r *bufio.Reader, s *Schema) {
	ReadByte(r, "diff size")
	d.OldValue = ReadByte(r, "old value")
	d.NewValue = ReadByte(r, "new value")
}

type Uint8Diff struct {
	AttributeDiffBase
	OldValue uint8
	NewValue uint8
}

func NewBaseUint8Diff(typeId, attributeId uint32, persistable bool) AttributeDiff {
	d := new(Uint8Diff)
	d.AttributeDiffBase.init(typeId, attributeId, persistable)
	return d
}

func NewUint8Diff(typeId, attributeId uint32, persistable bool, oldValue, newValue uint8) AttributeDiff {
	d := new(Uint8Diff)
	d.AttributeDiffBase.init(typeId, attributeId, persistable)
	d.OldValue = oldValue
	d.NewValue = newValue
	return d
}
func NewUint8SliceAddition(newValue uint8) AttributeDiff {
	d := new(Uint8Diff)
	d.NewValue = newValue
	return d
}

func NewUint8SliceModification(oldValue, newValue uint8) AttributeDiff {
	d := new(Uint8Diff)
	d.OldValue = oldValue
	d.NewValue = newValue
	return d
}

func (d *Uint8Diff) serialize(w *bufio.Writer, isPersistentWrite bool) {
	d.AttributeDiffBase.serialize(w, isPersistentWrite)
	WriteByte(1+1, w, "diff length")
	WriteUint8(d.OldValue, w, "old value")
	WriteUint8(d.NewValue, w, "new value")
}

func (d *Uint8Diff) deserialize(r *bufio.Reader, s *Schema) {
	ReadByte(r, "diff size")
	d.OldValue = ReadUint8(r, "old value")
	d.NewValue = ReadUint8(r, "new value")
}

type Uint16Diff struct {
	AttributeDiffBase
	OldValue uint16
	NewValue uint16
}

func NewBaseUint16Diff(typeId, attributeId uint32, persistable bool) AttributeDiff {
	d := new(Uint16Diff)
	d.AttributeDiffBase.init(typeId, attributeId, persistable)
	return d
}

func NewUint16Diff(typeId, attributeId uint32, persistable bool, oldValue, newValue uint16) AttributeDiff {
	d := new(Uint16Diff)
	d.AttributeDiffBase.init(typeId, attributeId, persistable)
	d.OldValue = oldValue
	d.NewValue = newValue
	return d
}
func NewUint16SliceAddition(newValue uint16) AttributeDiff {
	d := new(Uint16Diff)
	d.NewValue = newValue
	return d
}

func NewUint16SliceModification(oldValue, newValue uint16) AttributeDiff {
	d := new(Uint16Diff)
	d.OldValue = oldValue
	d.NewValue = newValue
	return d
}

func (d *Uint16Diff) serialize(w *bufio.Writer, isPersistentWrite bool) {
	d.AttributeDiffBase.serialize(w, isPersistentWrite)
	WriteByte(2+2, w, "diff length")
	WriteUint16(d.OldValue, w, "old value")
	WriteUint16(d.NewValue, w, "new value")
}

func (d *Uint16Diff) deserialize(r *bufio.Reader, s *Schema) {
	ReadByte(r, "diff size")
	d.OldValue = ReadUint16(r, "old value")
	d.NewValue = ReadUint16(r, "new value")
}

type Uint32Diff struct {
	AttributeDiffBase
	OldValue uint32
	NewValue uint32
}

func NewBaseUint32Diff(typeId, attributeId uint32, persistable bool) AttributeDiff {
	d := new(Uint32Diff)
	d.AttributeDiffBase.init(typeId, attributeId, persistable)
	return d
}

func NewUint32Diff(typeId, attributeId uint32, persistable bool, oldValue, newValue uint32) AttributeDiff {
	d := new(Uint32Diff)
	d.AttributeDiffBase.init(typeId, attributeId, persistable)
	d.OldValue = oldValue
	d.NewValue = newValue
	return d
}

func NewUint32SliceAddition(newValue uint32) AttributeDiff {
	d := new(Uint32Diff)
	d.NewValue = newValue
	return d
}

func NewUint32SliceModification(oldValue, newValue uint32) AttributeDiff {
	d := new(Uint32Diff)
	d.OldValue = oldValue
	d.NewValue = newValue
	return d
}

func (d *Uint32Diff) serialize(w *bufio.Writer, isPersistentWrite bool) {
	d.AttributeDiffBase.serialize(w, isPersistentWrite)
	WriteByte(4+4, w, "diff length")
	WriteUint32(d.OldValue, w, "old value")
	WriteUint32(d.NewValue, w, "new value")
}

func (d *Uint32Diff) deserialize(r *bufio.Reader, s *Schema) {
	ReadByte(r, "diff size")
	d.OldValue = ReadUint32(r, "old value")
	d.NewValue = ReadUint32(r, "new value")
}

type Uint64Diff struct {
	AttributeDiffBase
	OldValue uint64
	NewValue uint64
}

func NewBaseUint64Diff(typeId, attributeId uint32, persistable bool) AttributeDiff {
	d := new(Uint64Diff)
	d.AttributeDiffBase.init(typeId, attributeId, persistable)
	return d
}

func NewUint64Diff(typeId, attributeId uint32, persistable bool, oldValue, newValue uint64) AttributeDiff {
	d := new(Uint64Diff)
	d.AttributeDiffBase.init(typeId, attributeId, persistable)
	d.OldValue = oldValue
	d.NewValue = newValue
	return d
}

func NewUint64SliceAddition(newValue uint64) AttributeDiff {
	d := new(Uint64Diff)
	d.NewValue = newValue
	return d
}

func NewUint64SliceModification(oldValue, newValue uint64) AttributeDiff {
	d := new(Uint64Diff)
	d.OldValue = oldValue
	d.NewValue = newValue
	return d
}

func (d *Uint64Diff) serialize(w *bufio.Writer, isPersistentWrite bool) {
	d.AttributeDiffBase.serialize(w, isPersistentWrite)
	WriteByte(32+32, w, "diff length")
	WriteUint64(d.OldValue, w, "old value")
	WriteUint64(d.NewValue, w, "new value")
}

func (d *Uint64Diff) deserialize(r *bufio.Reader, s *Schema) {
	ReadByte(r, "diff size")
	d.OldValue = ReadUint64(r, "old value")
	d.NewValue = ReadUint64(r, "new value")
}

type Int8Diff struct {
	AttributeDiffBase
	OldValue int8
	NewValue int8
}

func NewBaseInt8Diff(typeId, attributeId uint32, persistable bool) AttributeDiff {
	d := new(Int8Diff)
	d.AttributeDiffBase.init(typeId, attributeId, persistable)
	return d
}

func NewInt8Diff(typeId, attributeId uint32, persistable bool, oldValue, newValue int8) AttributeDiff {
	d := new(Int8Diff)
	d.AttributeDiffBase.init(typeId, attributeId, persistable)
	d.OldValue = oldValue
	d.NewValue = newValue
	return d
}

func NewInt8SliceAddition(newValue int8) AttributeDiff {
	d := new(Int8Diff)
	d.NewValue = newValue
	return d
}

func NewInt8SliceModification(oldValue, newValue int8) AttributeDiff {
	d := new(Int8Diff)
	d.OldValue = oldValue
	d.NewValue = newValue
	return d
}

func (d *Int8Diff) serialize(w *bufio.Writer, isPersistentWrite bool) {
	d.AttributeDiffBase.serialize(w, isPersistentWrite)
	WriteByte(1+1, w, "diff length")
	WriteInt8(d.OldValue, w, "old value")
	WriteInt8(d.NewValue, w, "new value")
}

func (d *Int8Diff) deserialize(r *bufio.Reader, s *Schema) {
	ReadByte(r, "diff size")
	d.OldValue = ReadInt8(r, "old value")
	d.NewValue = ReadInt8(r, "new value")
}

type Int16Diff struct {
	AttributeDiffBase
	OldValue int16
	NewValue int16
}

func NewBaseInt16Diff(typeId, attributeId uint32, persistable bool) AttributeDiff {
	d := new(Int16Diff)
	d.AttributeDiffBase.init(typeId, attributeId, persistable)
	return d
}

func NewInt16Diff(typeId, attributeId uint32, persistable bool, oldValue, newValue int16) AttributeDiff {
	d := new(Int16Diff)
	d.AttributeDiffBase.init(typeId, attributeId, persistable)
	d.OldValue = oldValue
	d.NewValue = newValue
	return d
}

func NewInt16SliceAddition(newValue int16) AttributeDiff {
	d := new(Int16Diff)
	d.NewValue = newValue
	return d
}

func NewInt16SliceModification(oldValue, newValue int16) AttributeDiff {
	d := new(Int16Diff)
	d.OldValue = oldValue
	d.NewValue = newValue
	return d
}

func (d *Int16Diff) serialize(w *bufio.Writer, isPersistentWrite bool) {
	d.AttributeDiffBase.serialize(w, isPersistentWrite)
	WriteByte(2+2, w, "diff length")
	WriteInt16(d.OldValue, w, "old value")
	WriteInt16(d.NewValue, w, "new value")
}

func (d *Int16Diff) deserialize(r *bufio.Reader, s *Schema) {
	ReadByte(r, "diff size")
	d.OldValue = ReadInt16(r, "old value")
	d.NewValue = ReadInt16(r, "new value")
}

type Int32Diff struct {
	AttributeDiffBase
	OldValue int32
	NewValue int32
}

func NewBaseInt32Diff(typeId, attributeId uint32, persistable bool) AttributeDiff {
	d := new(Int32Diff)
	d.AttributeDiffBase.init(typeId, attributeId, persistable)
	return d
}

func NewInt32Diff(typeId, attributeId uint32, persistable bool, oldValue, newValue int32) AttributeDiff {
	d := new(Int32Diff)
	d.AttributeDiffBase.init(typeId, attributeId, persistable)
	d.OldValue = oldValue
	d.NewValue = newValue
	return d
}

func NewInt32SliceAddition(newValue int32) AttributeDiff {
	d := new(Int32Diff)
	d.NewValue = newValue
	return d
}

func NewInt32SliceModification(oldValue, newValue int32) AttributeDiff {
	d := new(Int32Diff)
	d.OldValue = oldValue
	d.NewValue = newValue
	return d
}

func (d *Int32Diff) serialize(w *bufio.Writer, isPersistentWrite bool) {
	d.AttributeDiffBase.serialize(w, isPersistentWrite)
	WriteByte(4+4, w, "diff length")
	WriteInt32(d.OldValue, w, "old value")
	WriteInt32(d.NewValue, w, "new value")
}

func (d *Int32Diff) deserialize(r *bufio.Reader, s *Schema) {
	ReadByte(r, "diff size")
	d.OldValue = ReadInt32(r, "old value")
	d.NewValue = ReadInt32(r, "new value")
}

type Int64Diff struct {
	AttributeDiffBase
	OldValue int64
	NewValue int64
}

func NewBaseInt64Diff(typeId, attributeId uint32, persistable bool) AttributeDiff {
	d := new(Int64Diff)
	d.AttributeDiffBase.init(typeId, attributeId, persistable)
	return d
}

func NewInt64Diff(typeId, attributeId uint32, persistable bool, oldValue, newValue int64) AttributeDiff {
	d := new(Int64Diff)
	d.AttributeDiffBase.init(typeId, attributeId, persistable)
	d.OldValue = oldValue
	d.NewValue = newValue
	return d
}

func NewInt64SliceAddition(newValue int64) AttributeDiff {
	d := new(Int64Diff)
	d.NewValue = newValue
	return d
}

func NewInt64SliceModification(oldValue, newValue int64) AttributeDiff {
	d := new(Int64Diff)
	d.OldValue = oldValue
	d.NewValue = newValue
	return d
}

func (d *Int64Diff) serialize(w *bufio.Writer, isPersistentWrite bool) {
	d.AttributeDiffBase.serialize(w, isPersistentWrite)
	WriteByte(32+32, w, "diff length")
	WriteInt64(d.OldValue, w, "old value")
	WriteInt64(d.NewValue, w, "new value")
}

func (d *Int64Diff) deserialize(r *bufio.Reader, s *Schema) {
	ReadByte(r, "diff size")
	d.OldValue = ReadInt64(r, "old value")
	d.NewValue = ReadInt64(r, "new value")
}

type Float32Diff struct {
	AttributeDiffBase
	OldValue float32
	NewValue float32
}

func NewBaseFloat32Diff(typeId, attributeId uint32, persistable bool) AttributeDiff {
	d := new(Float32Diff)
	d.AttributeDiffBase.init(typeId, attributeId, persistable)
	return d
}

func NewFloat32Diff(typeId, attributeId uint32, persistable bool, oldValue, newValue float32) AttributeDiff {
	d := new(Float32Diff)
	d.AttributeDiffBase.init(typeId, attributeId, persistable)
	d.OldValue = oldValue
	d.NewValue = newValue
	return d
}

func NewFloat32SliceAddition(newValue float32) AttributeDiff {
	d := new(Float32Diff)
	d.NewValue = newValue
	return d
}

func NewFloat32SliceModification(oldValue, newValue float32) AttributeDiff {
	d := new(Float32Diff)
	d.OldValue = oldValue
	d.NewValue = newValue
	return d
}

func (d *Float32Diff) serialize(w *bufio.Writer, isPersistentWrite bool) {
	d.AttributeDiffBase.serialize(w, isPersistentWrite)
	WriteByte(4+4, w, "diff length")
	WriteFloat32(d.OldValue, w, "old value")
	WriteFloat32(d.NewValue, w, "new value")
}

func (d *Float32Diff) deserialize(r *bufio.Reader, s *Schema) {
	ReadByte(r, "diff size")
	d.OldValue = ReadFloat32(r, "old value")
	d.NewValue = ReadFloat32(r, "new value")
}

type Float64Diff struct {
	AttributeDiffBase
	OldValue float64
	NewValue float64
}

func NewBaseFloat64Diff(typeId, attributeId uint32, persistable bool) AttributeDiff {
	d := new(Float64Diff)
	d.AttributeDiffBase.init(typeId, attributeId, persistable)
	return d
}

func NewFloat64Diff(typeId, attributeId uint32, persistable bool, oldValue, newValue float64) AttributeDiff {
	d := new(Float64Diff)
	d.AttributeDiffBase.init(typeId, attributeId, persistable)
	d.OldValue = oldValue
	d.NewValue = newValue
	return d
}

func NewFloat64SliceAddition(newValue float64) AttributeDiff {
	d := new(Float64Diff)
	d.NewValue = newValue
	return d
}

func NewFloat64SliceModification(oldValue, newValue float64) AttributeDiff {
	d := new(Float64Diff)
	d.OldValue = oldValue
	d.NewValue = newValue
	return d
}

func (d *Float64Diff) serialize(w *bufio.Writer, isPersistentWrite bool) {
	d.AttributeDiffBase.serialize(w, isPersistentWrite)
	WriteByte(32+32, w, "diff length")
	WriteFloat64(d.OldValue, w, "old value")
	WriteFloat64(d.NewValue, w, "new value")
}

func (d *Float64Diff) deserialize(r *bufio.Reader, s *Schema) {
	ReadByte(r, "diff size")
	d.OldValue = ReadFloat64(r, "old value")
	d.NewValue = ReadFloat64(r, "new value")
}

type StringDiff struct {
	AttributeDiffBase
	OldValue string
	NewValue string
}

func NewBaseStringDiff(typeId, attributeId uint32, persistable bool) AttributeDiff {
	d := new(StringDiff)
	d.AttributeDiffBase.init(typeId, attributeId, persistable)
	return d
}

func NewStringDiff(typeId, attributeId uint32, persistable bool, oldValue, newValue string) AttributeDiff {
	d := new(StringDiff)
	d.AttributeDiffBase.init(typeId, attributeId, persistable)
	d.OldValue = oldValue
	d.NewValue = newValue
	return d
}

func NewStringSliceAddition(newValue string) AttributeDiff {
	d := new(StringDiff)
	d.NewValue = newValue
	return d
}

func NewStringSliceModification(oldValue, newValue string) AttributeDiff {
	d := new(StringDiff)
	d.OldValue = oldValue
	d.NewValue = newValue
	return d
}

func (d *StringDiff) serialize(w *bufio.Writer, isPersistentWrite bool) {
	d.AttributeDiffBase.serialize(w, isPersistentWrite)
	WriteUint32(uint32(4+len(d.OldValue)+4+len(d.NewValue)), w, "diff length")
	WriteString(d.OldValue, w, "old value")
	WriteString(d.NewValue, w, "new value")
}

func (d *StringDiff) deserialize(r *bufio.Reader, s *Schema) {
	ReadByte(r, "diff size")
	d.OldValue = ReadString(r, "old value")
	d.NewValue = ReadString(r, "new value")
}

type TimeDiff struct {
	AttributeDiffBase
	OldValue time.Time
	NewValue time.Time
}

func NewBaseTimeDiff(typeId, attributeId uint32, persistable bool) AttributeDiff {
	d := new(TimeDiff)
	d.AttributeDiffBase.init(typeId, attributeId, persistable)
	return d
}

func NewTimeDiff(typeId, attributeId uint32, persistable bool, oldValue, newValue time.Time) AttributeDiff {
	d := new(TimeDiff)
	d.AttributeDiffBase.init(typeId, attributeId, persistable)
	d.OldValue = oldValue
	d.NewValue = newValue
	return d
}

func NewTimeSliceAddition(newValue time.Time) AttributeDiff {
	d := new(TimeDiff)
	d.NewValue = newValue
	return d
}

func NewTimeSliceModification(oldValue, newValue time.Time) AttributeDiff {
	d := new(TimeDiff)
	d.OldValue = oldValue
	d.NewValue = newValue
	return d
}

func (d *TimeDiff) serialize(w *bufio.Writer, isPersistentWrite bool) {
	d.AttributeDiffBase.serialize(w, isPersistentWrite)
	WriteByte(32+32+32+32, w, "diff length")
	WriteInt64(d.OldValue.Unix(), w, "old value seconds")
	WriteInt64(int64(d.OldValue.Nanosecond()), w, "old value nanoseconds")
	WriteInt64(d.NewValue.Unix(), w, "new value seconds")
	WriteInt64(int64(d.NewValue.Nanosecond()), w, "new value nanoseconds")
}

func (d *TimeDiff) deserialize(r *bufio.Reader, s *Schema) {
	ReadByte(r, "diff size")
	d.OldValue = time.Unix(ReadInt64(r, "old value seconds"), ReadInt64(r, "old value nanoseconds"))
	d.NewValue = time.Unix(ReadInt64(r, "new value seconds"), ReadInt64(r, "new value nanoseconds"))
}

type DiffOper uint32

const (
	DiffOper_add DiffOper = iota
	DiffOper_modify
	DiffOper_truncate
	DiffOper_delete
)

type SliceDiff struct {
	AttributeDiffBase
	Idx        uint32
	Oper       DiffOper
	ActualDiff AttributeDiff
}

func NewBaseSliceDiff(typeId, attributeId uint32, persistable bool) AttributeDiff {
	d := new(SliceDiff)
	d.AttributeDiffBase.init(typeId, attributeId, persistable)
	return d
}

func NewSliceDiff(typeId, attributeId uint32, persistable bool, idx uint32, oper DiffOper, actualDiff AttributeDiff) AttributeDiff {
	d := new(SliceDiff)
	d.AttributeDiffBase.init(typeId, attributeId, persistable)
	d.Idx = idx
	d.Oper = oper
	d.ActualDiff = actualDiff
	return d
}

func (d *SliceDiff) serialize(w *bufio.Writer, isPersistentWrite bool) {
	defer func() {
		if r := recover(); r != nil {
			panic(fmt.Sprintf("Couldn't encode slice diff, %d-%d, idx %d\nCause: %s", d.typeId, d.attributeId, d.Idx, r))
		}
	}()
	var tempBuf bytes.Buffer
	if d.Oper != DiffOper_truncate {
		tempWriter := bufio.NewWriter(&tempBuf)
		d.ActualDiff.serialize(tempWriter, isPersistentWrite)
		tempWriter.Flush()
	}
	d.AttributeDiffBase.serialize(w, isPersistentWrite)
	WriteUint32(uint32(tempBuf.Len()+4+4), w, "diff size")
	WriteUint32(d.Idx, w, "idx")
	WriteByte(byte(d.Oper), w, "oper")
	tempBuf.WriteTo(w)
}

func (d *SliceDiff) deserialize(r *bufio.Reader, s *Schema) {
	ReadUint32(r, "diff size")
	d.Idx = ReadUint32(r, "idx")
	oper := DiffOper(ReadByte(r, "oper"))
	switch oper {
	case DiffOper_add:
	case DiffOper_modify:
	case DiffOper_truncate:
		d.Oper = oper
	default:
		panic(fmt.Sprintf("Invalid DiffOper %d", oper))
	}
	if oper != DiffOper_truncate {
		d.ActualDiff = deserializeAttributeDiff(r, s)
	}
}

type StructDiff struct {
	AttributeDiffBase
	Diff *MDODiff
}

func NewBaseStructDiff(typeId, attributeId uint32, persistable bool) AttributeDiff {
	d := new(StructDiff)
	d.AttributeDiffBase.init(typeId, attributeId, persistable)
	return d
}

func NewStructDiff(typeId, attributeId uint32, persistable bool, diff *MDODiff) AttributeDiff {
	d := new(StructDiff)
	d.AttributeDiffBase.init(typeId, attributeId, persistable)
	d.Diff = diff
	return d
}

func (d *StructDiff) serialize(w *bufio.Writer, isPersistentWrite bool) {
	d.AttributeDiffBase.serialize(w, isPersistentWrite)
	d.Diff.Serialize(w, isPersistentWrite)
}

func (d *StructDiff) deserialize(r *bufio.Reader, s *Schema) {
	d.Diff = DeserializeMDODiff(r, s)
}
